/*
 *
 * Copyright(c) 2010 Safelayer Secure Communications
 *
 * This is open source software, placed under the terms of the
 * GNU GENERAL PUBLIC LICENSE Version 3.
 * Please see: http://www.gnu.org/licenses/gpl-3.0.txt
 * 
 * Permission to use, copy, modify, and distribute this software
 * for any purpose and without fee is hereby granted, provided
 * that the above copyright notices appear in all copies and that
 * both the copyright notice and this permission notice appear in
 * supporting documentation.
 * 
 */
package com.neuralnoise.foaf.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFWriter;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.DC;
import com.hp.hpl.jena.vocabulary.RDF;

/**
 * Class which serializes FOAF person instances to RDF/XML file or outputStream.
 * 
 * @author victor.martinez <swtp@safelayer.com>
 */
public class FOAFWriter {

	private PersonalProfileDocumentProperties documentProperties;

	private OutputStream out;

	private String base;

	private Model model;

	private Resource primaryPerson;

	private FOAFWriter(File file, Model model, String base) throws FileNotFoundException {
		this.out = new FileOutputStream(file);
		this.model = model;
		this.base = base;
	}

	private FOAFWriter(OutputStream out, Model model, String base) {
		this.out = out;
		this.model = model;
		this.base = base;
	}

	/* static factory methods */

	/**
	 * Creates a new instance of this class given a file.
	 * 
	 * @param file a File object to write to.
	 * @param base This is used for relative URIs that would be resolved against the document
	 *            retrieval URL.
	 * 
	 * @return an instance of this class from a <code>url</code>.
	 */
	public static FOAFWriter newFileWriter(File file, String base) throws FileNotFoundException {
		return new FOAFWriter(file, ModelFactory.createDefaultModel(), base);
	}

	/**
	 * Creates a new instance of this class given an outputStream.
	 * 
	 * @param out an outputStream object to write to.
	 * @param base This is used for relative URIs that would be resolved against the document
	 *            retrieval URL.
	 * 
	 * @return an instance of this class from a <code>url</code>.
	 */
	public static FOAFWriter newStreamWriter(OutputStream out, String base) {
		return new FOAFWriter(out, ModelFactory.createDefaultModel(), base);
	}

	/**
	 * Serializes a FOAF person instance to an RDF memory model.
	 * 
	 * @param person the instance {@link Person} instance to serialize.
	 */
	public void writePerson(Person person) {
		primaryPerson = FOAFConverter.fromPerson(person, model);
	}

	/**
	 * Flushes and closes the stream.
	 * 
	 * @throws IOException
	 */
	public void flush() throws IOException {
		if (primaryPerson == null)
			throw new IllegalStateException("not write method has been invoked");
		if (documentProperties == null) // lazy loading
			documentProperties = new PersonalProfileDocumentProperties();
		addPersonalProfileDocument();
		getWriter().write(model, out, base);
		out.flush();
		out.close();
	}

	// Helpers -----------------------------------------------------------------------------------

	/**
	 * Returns the internal {@link RDFWriter} customized for printing FOAFs. The RDFWriter is 
	 * configured to be more lenient writing RDF graphs and also add all necessary prefixes 
	 * used by FOAF vocabulary.
	 * 
	 * @return A customized RDFWriter for printing FOAFs.
	 */
	private RDFWriter getWriter() {
		addFOAFNsPrefixs();
		RDFWriter writer = model.getWriter("RDF/XML-ABBREV");
		writer.setProperty("allowBadURIs", "true"); // "lenient" mode
		writer.setProperty("prettyTypes", new Resource[] { FOAFVocabulary.Person });
		return writer;
	}

	/**
	 * Builds a Personal Profile Document.
	 * 
	 */
	private void addPersonalProfileDocument() {

		Resource documentResource = model.createResource(base);
		documentResource.addProperty(RDF.type, FOAFVocabulary.PersonalProfileDocument);
		documentResource.addProperty(FOAFVocabulary.primaryTopic, primaryPerson);
		Resource apiResource = model.createResource(documentProperties.getApiProperty());
		Resource contactResource = model.createResource(documentProperties.getContactProperty());
		documentResource.addProperty(model.createProperty("http://webns.net/mvcb/", "generatorAgent"), apiResource);
		documentResource.addProperty(model.createProperty("http://webns.net/mvcb/", "errorReportsTo"), contactResource);
		documentResource.addProperty(DC.description, documentProperties.getDescriptionProperty());
	}

	/**
	 * Adds all namespaces needed to represent terms related with FOAF vocabulary. Note, all the
	 * namespaces added can be mapped to FOAF java classes which models the FOAF vocabulary.
	 * 
	 */
	private void addFOAFNsPrefixs() {
		model.getGraph().getPrefixMapping().setNsPrefix("foaf", FOAFVocabulary.getNsUri());
		model.getGraph().getPrefixMapping().setNsPrefix("dc", DC.getURI());
		model.getGraph().getPrefixMapping().setNsPrefix("admin", "http://webns.net/mvcb/");
	}

}
